/* 
 * Copyright (c) Sergey B. Berezin (s_berezin@cs.msu.su), 2002, 2003.
 *
 * You may freely distribute or modify this source code retaining the 
 * above copyright notice.
 *
 * This software is provided "as-is" and without guarantee or warrantee 
 * of any kind, expressed or implied :)
 */

using System;
using System.Drawing;
using System.Diagnostics;
using System.Text;

using ManagedGL;
using VisualEsse.DataEngine.Types;

namespace UVS.Base {

	public struct BoxD {
		public RangeD x;
		public RangeD y;
		public RangeD z;
		
		public double Xmin {
			get { return x.min; }
		}
		
		public double Xmax {
			get { return x.max;	}
		}

		public double Ymin {
			get { return y.min;	}
		}
		
		public double Ymax {
			get { return y.max; }
		}

		public double Zmin {
			get { return z.min;	}
		}

		public double Zmax {
			get { return z.max;	}
		}

		public BoxD(double xmin,double ymin,double zmin,double xmax,double ymax,double zmax) {
			x = new RangeD(xmin,xmax);
			y = new RangeD(ymin,ymax);
			z = new RangeD(zmin,zmax);
		}

		public BoxD(RectangleD rect,double zmin,double zmax) {
			x = new RangeD(rect.Xmin,rect.Xmax);
			y = new RangeD(rect.Ymin,rect.Ymax);
			z = new RangeD(zmin,zmax);
		}
		
	/*	  /// <summary>Constructs bounding box for specified rectangular scalar field.
		/// X and Y ranges are taken from X and Y grids and Z range is taken
		/// from grid data</summary>
		/// <param name="field">Field to construct bounding box</param>
		public BoxD(INonUniformScalarField2d data) {
			x = new RangeD(data.Grid.X);
			y = new RangeD(data.Grid.Y);
			z = new RangeD(data.Data);
		}*/

		/*public BoxD(IScalarRectField data,IGrid2d grid) {
			x = new RangeD(grid,true);
			y = new RangeD(grid,false);
			z = new RangeD(data);
		}*/

		public BoxD(INonUniformGrid3d grid) {
			x = new RangeD(grid.X);
			y = new RangeD(grid.Y);
			z = new RangeD(grid.Z);
		}

		public double Width {
			get { return (x.max - x.min); }
		}

		public double Height {
			get { return (y.max - y.min); }
		}

		public double Depth {
			get { return (z.max - z.min); }
		}

		public double CenterX {
			get { return (x.max + x.min)/2; }
		}

		public double CenterY {
			get { return (y.max + y.min)/2; }
		}

		public double CenterZ {
			get { return (z.max + z.min)/2; }
		}

		public bool IsEmpty {
			get { return (Xmin >= Xmax && Ymin >= Ymax && Zmin >= Zmax); }
		}

		public double Radius {
			get {
				return Math.Sqrt(Width*Width + Height*Height + Depth*Depth)/2;
			}
		}

		public void Unite(BoxD another) {
			x.Unite(another.x);
			y.Unite(another.y);
			z.Unite(another.z);
		}

		/*
		 * Next three methods returns coordinates of specified vertex.
		 * Vertex is defined by bitfield, where 1st bit corresponds to X axis,
		 * 2nd bit -- to Y axis and 3rd bit -- to Z axis.
		 */

		public double GetX(int flags) {
			return ((flags & 1) == 0)?(x.min):(x.max);
		}

		public double GetY(int flags) {
			return ((flags & 2) == 0)?(y.min):(y.max);
		}

		public double GetZ(int flags) {
			return ((flags & 4) == 0)?(z.min):(z.max);
		}

		/// <summary>Performs linear transformation from this box to dst</summary>
		/// <param name="v">Transformed vertex</param>
		/// <param name="dst">Destination box</param>
		/// <returns>Returns transformed vertex</returns>
		public ManagedGL.Vector3d Scale(ManagedGL.Vector3d v,BoxD dst) {
			ManagedGL.Vector3d result;
			result.x = x.Scale(v.x,dst.x);
			result.y = y.Scale(v.y,dst.y);
			result.z = z.Scale(v.z,dst.z);
			return result;
		}

		public Matrix4x4 GetTransformMatrix(BoxD dst) {
			Matrix4x4 result = Matrix4x4.Translate(dst.Xmin,dst.Ymin,dst.Zmin);
			result.Multiply(Matrix4x4.Scale(dst.Width/Width,dst.Height/Height,dst.Depth/Depth));
			result.Multiply(Matrix4x4.Translate(-Xmin,-Ymin,-Zmin));
			return result;
		}

		/// <summary>Increases dimensions of bounding box so that each side will be not less
		/// than other sides muliplied by corresponding aspect</summary>
		/// <param name="xa">X aspect ratio</param>
		/// <param name="ya">Y aspect ratio</param>
		/// <param name="za">Z aspect ratio</param>
		public void UpdateAspects(double xa,double ya,double za) {
			Debug.Assert(xa >= 1 && ya >= 1 && za >= 1);
			while(true) {
				double xmax = Math.Max(Height,Depth);
				double ymax = Math.Max(Width,Depth);
				double zmax = Math.Max(Width,Height);
				Debug.Assert(xmax > 0 && ymax > 0 && zmax > 0); // At least two dimensions should be positive					  
				double xi = Width/xmax;
				double yi = Height/ymax;
				double zi = Depth/zmax;
				if((xi <= yi && yi < zi || zi <= yi && yi < xi) && yi*ya < 0.99) { 
					double delta = ymax/ya - Height;  // Height needs to be updated
					Debug.Assert(delta > 0);
					y.Increase(delta);
				} else if((xi <= zi && zi < yi || yi <= zi && zi < xi) && zi*za < 0.99) {
					double delta = zmax/za - Depth;   // Depth needs to be updated
					Debug.Assert(delta > 0);
					z.Increase(delta);
				} else if((yi <= xi && xi < zi || zi <= xi && xi < yi) && xi*xa < 0.99) {
					double delta = xmax/xa - Width;   // Width needs to be updated
					Debug.Assert(delta > 0);
					x.Increase(delta);
				} else if(yi < xi && yi < zi && yi*ya < 0.99) { 
					double delta = ymax/ya - Height;  // Height needs to be updated
					Debug.Assert(delta > 0);
					y.Increase(delta);
					break;
				} else if(zi < xi && zi < yi && zi*za < 0.99) {
					double delta = zmax/za - Depth;   // Depth needs to be updated
					Debug.Assert(delta > 0);
					z.Increase(delta);
					break;
				} else if(xi < yi && xi < zi && xi*xa < 0.99) {
					double delta = xmax/xa - Width;   // Width needs to be updated
					Debug.Assert(delta > 0);
					x.Increase(delta);
					break;
				} else
					break;
			}
		}

        public override string ToString()
        {
            return String.Format("{0}x{1}x{2}", x, y, z);
        }

		/// <summary>Empty box singleton</summary>
		static private BoxD emptyBox = new BoxD(0,0,0,0,0,0);

		/// <summary>Returns empty unique box</summary>
		static public BoxD Empty {
			get {
				return emptyBox;
			}
		}
	}
}
